#include <bits/stdc++.h>
using namespace std;
//C++14 
/*当引用计数为0时，就会释放所指向的堆内存，
通过引用计数虽然解决了指针独占的问题，但也引来了引用成环的问题
*/
template <typename T>
class SharedPtr{
public:
    SharedPtr():m_data(nullptr){}
    SharedPtr(T* data):m_data(data){
        if(m_data != nullptr){
            m_count = new int(1);
        }
    }
    SharedPtr(SharedPtr& other):m_data(other.m_data),m_count(other.m_count){
        if(m_data != nullptr){
            (*m_count)++;
        }
    }
    SharedPtr(SharedPtr&& other)noexcept:m_data(other.m_data()),m_count(other.m_count){
        other.m_data = nullptr;
        other.m_count = nullptr;
    }    
    ~SharedPtr(){
        if(m_data != nullptr){
            (*m_count)--;
            if(*m_count <= 0){
                delete m_data;
                m_data = nullptr;   
                delete m_count;
                m_count = nullptr;             
            }
            delete m_data;
            m_data = nullptr;
        }
    }

    T* get(){
        return m_data;
    }

    //对象所有权被转移，对象本身不占有该指针
    T* release(){
        auto data = m_data;
        m_data = nullptr;
        return data;
    }

    //赋值新指针
    void reset(T* data = nullptr){
        if(m_data == data){
            return;
        }
        if(m_data == nullptr){
            if(data != nullptr){
                m_data = data;
                m_count = new int(1);
            }
            return;
        }
        (*m_count)--;
        if(*m_count <= 0){
            delete m_data;
            m_data= nullptr;
            delete m_count;
            m_count = nullptr;
        }
        m_data =data;
        if(data != nullptr){
            m_count = new int(1);
        }
    }

    int use_count() const{
        if(m_data == nullptr){
            return 0;
        }
        return *m_count;
    }

    bool unique() const{
        if(m_data == nullptr){
            return false;
        }
        return *m_count == 1;
    }

    void swap(SharedPtr<T>& other){
        auto data = other.m_data;
        other.m_data = m_data;
        m_data = data;
        auto count = other.m_count;
        other.m_count = m_count;
        m_count = count;
    }

    T* operator -> (){
        return m_data;
    } 
    T& operator * (){
        return *m_data;
    } 

    explicit operator bool() const noexcept{
        return m_data != nullptr;
    }

    SharedPtr& operator = (const SharedPtr<T>& other){
        if(this == &other){
            return *this;
        }
        m_data = other.m_data;
        m_count = other.m_count;
        (*m_count)++;
        return *this;
    }

    SharedPtr & operator = (SharedPtr<T>&& other) noexcept{
        if(this == &other){
            return *this;
        }
        m_data = other.m_data;
        m_count = other.m_count;
        other.m_data = nullptr;
        other.m_count = nullptr;
        (*m_count)++;
        return *this;
    }

    //对数组包装
    T& operator [] (int i) const{   //重载下标运算符
        return m_data[i];
    }

private:
    T* m_data;
    int* m_count;
};

class Test{
public:
    Test() = default;
    ~Test(){
        std::cout<<"test is deleted"<<std::endl;
    }
    void name(const string& name){
        m_name = name;
    }
    string name() const{
        return m_name;
    }

private:
    string m_name;
};

int main(){

    auto p = new Test();
    SharedPtr<Test> sp(p);
    //print:test is deleted
    p->name("jack");
    cout<<p->name()<<endl;
    sp->name("tom");
    cout<<sp->name()<<endl;
    //共享test对象，只析构一次

    SharedPtr<Test> sp3;
    sp3 = sp;
    cout<<sp3->name()<<endl; 
    
    SharedPtr<Test> sp2(sp);
    cout<<sp2->name()<<endl;   
    
    cout<<sp.use_count()<<endl;
    cout<<sp2.use_count()<<endl;

    return 0;
}